﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Performance
{
    /// <summary>
    /// A performance Metric tracks a single performance events start, stop, and elapsed times
    /// </summary>
    public class PerformanceMetric
    {
        public string MetricName;
        public TimeSpan MetricTimeSpan;
        public DateTime MetricTimeStarted;
        public DateTime MetricTimeEnded;
        public string Data;
    }

    /// <summary>
    /// This static class is meant to make it easy to get performance information. Call 'start' and 'stop' with the same string
    /// to store performance data into that string.  You can track multiple items at once, and then retrieve a single or full
    /// report, as well as save it to disk.
    /// </summary>
    static public class PerformanceManager
    {
        private static Dictionary<string, PerformanceMetric> metrics = new Dictionary<string, PerformanceMetric>();


        /// <summary>
        /// Start measuring a time metric. Pass in any string you wish, but use the same string when calling StopMeasuring
        /// </summary>
        /// <param name="MetricToMeasure"></param>
        static public void StartMeasuring(string MetricToMeasure)
        {
            PerformanceMetric metric;
            if (metrics.ContainsKey(MetricToMeasure))
            {
                metrics[MetricToMeasure].MetricTimeStarted = DateTime.Now;
                metrics[MetricToMeasure].Data = null;
            }
            else
            {
                metric = new PerformanceMetric();
                metric.MetricTimeStarted = DateTime.Now;
                metrics.Add(MetricToMeasure, metric);
            }
        }

        /// <summary>
        /// Allows extra data to be appended to a measurement
        /// </summary>
        /// <param name="MetricToMeasure"></param>
        /// <param name="DataToAdd"></param>
        static public void AddData(string MetricToMeasure, string DataToAdd)
        {
            if (metrics.ContainsKey(MetricToMeasure))
            {
                metrics[MetricToMeasure].Data += DataToAdd;
            }
        }

        /// <summary>
        /// Called to stop measuring a time metric. Returns the metrics timespan for ease of reporting
        /// </summary>
        /// <param name="MetricToMeasure"></param>
        /// <returns></returns>
        static public TimeSpan StopMeasuring(string MetricToMeasure)
        {
            PerformanceMetric metric;
            if (metrics.ContainsKey(MetricToMeasure))
            {
                metric = metrics[MetricToMeasure];
            }
            else
            {
                throw new Exception("StopMeasuring was called against a metric which was never created/started");
            }

            metric.MetricTimeEnded = DateTime.Now;
            metric.MetricTimeSpan = metric.MetricTimeEnded - metric.MetricTimeStarted;

            return metric.MetricTimeSpan;
        }

        /// <summary>
        /// Resets metrics that the class has been keeping track of
        /// </summary>
        static public void ClearPerformanceData()
        {
            metrics.Clear();
        }

        /// <summary>
        /// Pass in the string 'metricName' you called start/stop on to get back the result
        /// </summary>
        /// <param name="MetricToMeasure"></param>
        /// <returns></returns>
        static public string GetPerformanceReport(string MetricToMeasure)
        {
            string MeasurementString = "";
            PerformanceMetric metric;
            if (metrics.ContainsKey(MetricToMeasure))
            {
                metric = metrics[MetricToMeasure];
            }
            else
            {
                return "No Metric found for: " + MetricToMeasure;
            }
            if (metric.MetricTimeSpan.TotalMilliseconds < 1)
            {
                    
                MeasurementString = MetricToMeasure + " Measurement: " + metric.MetricTimeSpan.Ticks + "ticks ";
            }
            else if (metric.MetricTimeSpan.TotalMilliseconds > 1000)
            {
                MeasurementString = MetricToMeasure + " Measurement: " + metric.MetricTimeSpan.TotalSeconds + "secs ";
            }
            else
            {
                MeasurementString = MetricToMeasure + " Measurement: " + metric.MetricTimeSpan.TotalMilliseconds + "ms ";
            }
            if (metric.Data != null)
            {
                MeasurementString += "Data: " + metric.Data;
             }
            MeasurementString += "\r\n";
            return MeasurementString;

        }

        /// <summary>
        /// Returns all currently monitored performance data
        /// </summary>
        /// <returns></returns>
        static public string GetAllPerformanceData()
        {
            string report = "";
            foreach (KeyValuePair<string, PerformanceMetric> metric in metrics)
            {
                report += GetPerformanceReport(metric.Key);
            }
            return report;
        }

        /// <summary>
        /// Stores performance data to disk. useful for debugging when UI is not available
        /// </summary>
        static void SaveToDisk()
        {
            TextWriter stream = new StreamWriter("Performance.Log");
            stream.Write(GetAllPerformanceData());
            stream.Close();
        }

    }

}
